import math
import random
import os
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *

def resize((width, height)):
    if height==0:
        height=1
    glViewport(0, 0, width, height)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(45, 1.0*width/height, 0.1, 200.0)
    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

def init(RESOLUTION=(640,480)):
    video_flags = OPENGL|DOUBLEBUF#|FULLSCREEN
    pygame.init()
    pygame.display.set_caption("Crazy Space Killyness!!")
    pygame.display.set_mode(RESOLUTION, video_flags)
    resize(RESOLUTION)
    light_ambient = (0.5, 0.5, 0.5, 1)
    light_diffuse = (1, 1, 1, 1)
    light_position = (0, 0, 0, 1)
    glEnable(GL_TEXTURE_2D)
    glShadeModel(GL_SMOOTH)
    glClearColor(0.0, 0.0, 0.0, 0.0)
    glClearDepth(1.0)
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LEQUAL)
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
    #glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient)
    #glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse)
    #glLightfv(GL_LIGHT1, GL_POSITION, light_position)
    #glEnable(GL_LIGHT1)
    #glEnable(GL_LIGHTING)
    glEnable(GL_CULL_FACE)
    #glEnable(GL_POINT_SMOOTH)
    #glBlendFunc(GL_SRC_ALPHA,GL_ONE)
    glAlphaFunc(GL_GREATER, 0.2)

def set_position(position, target, up):
    glLoadIdentity()
    #gluLookAt(0, 0, 0,
    #          target.x, target.y, target.z,
    #          up.x, up.y, up.z)
    #self.starfield.draw()
    glTranslatef(position.x, position.y, position.z)
    


def start_draw(position, r, hud):
    matrix = [ 1 - 2*r.v.y**2 - 2*r.v.z**2, 2*r.v.x*r.v.y - 2*r.w*r.v.z, 2*r.v.x*r.v.z + 2*r.w*r.v.y, 0,
               2*r.v.x*r.v.y + 2*r.w*r.v.z, 1 - 2*r.v.x**2 - 2*r.v.z**2, 2*r.v.y*r.v.z - 2*r.w*r.v.x, 0,
               2*r.v.x*r.v.z - 2*r.w*r.v.y, 2*r.v.y*r.v.z + 2*r.w*r.v.x, 1 - 2*r.v.x**2 - 2*r.v.y**2, 0,
               0,                           0,                           0,                           1 ]
    #set_position(position, rotation)
    glLoadIdentity()
    #glTranslatef(position.x, position.y, position.z)
    glMultMatrixd(matrix)
    glTranslatef(position.x, position.y, position.z)
    #glPushMatrix()
    #glLoadIdentity()
    #glTranslatef(0, 0, -20)
    glCallList(hud)
    #glPopMatrix()

def render():
    pygame.display.flip()
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

def draw(position, target, up, call_list):
    glPushMatrix()
    gluLookAt(position.x, position.y, position.z,
              target.x, target.y, target.z,
              up.x, up.y, up.z)
    glCallList(call_list)
    glPopMatrix()
    
def make_crosshair(fname='textures/crosshair.png'):
        textures = [0,0]
        texturefile = os.path.join(fname)
        textureSurface = pygame.image.load(texturefile)
        textureData = pygame.image.tostring(textureSurface, "RGBX", 1)
        glBindTexture(GL_TEXTURE_2D, textures[0])
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSurface.get_width(),
            textureSurface.get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
            textureData)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
        crosshair = glGenLists(1)
        glNewList(crosshair, GL_COMPILE)
        glEnable(GL_ALPHA_TEST)
        glDisable(GL_DEPTH_TEST)
        glBindTexture(GL_TEXTURE_2D, textures[0])
        glBegin(GL_QUADS)

        # Front Face (note that the texture's corners have to match the quad'scorners)
        glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0,  1.0)    # Bottom Left Of The Texture and Quad
        glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0,  1.0)    # Bottom Right Of The Texture and Quad
        glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0,  1.0)    # Top Right Of The Texture and Quad
        glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0,  1.0)    # Top Left Of The Texture and Quad

        glEnd()
        glEnable(GL_DEPTH_TEST)
        glDisable(GL_ALPHA_TEST)
        glEndList()
        return crosshair


class Camera(object):
    def __init__(self, ship, RESOLUTION=(640,480)):
        self.ship = ship
        video_flags = OPENGL|DOUBLEBUF#|FULLSCREEN
        pygame.init()
        pygame.display.set_caption("Crazy Space Killyness!!")
        pygame.display.set_mode(RESOLUTION, video_flags)
        self.resize(RESOLUTION)
        self.init()
        self.starfield = StarField(1500, (1.0, 1.0, 1.0),
                                   -100, [0,0], RESOLUTION)
        self.triangle = Triangle(0)
        self.make_crosshair()

    def init(self):
        light_ambient = (0.5, 0.5, 0.5, 1)
        light_diffuse = (1, 1, 1, 1)
        light_position = (0, 0, 0, 1)
        glEnable(GL_TEXTURE_2D)
        glShadeModel(GL_SMOOTH)
        glClearColor(0.0, 0.0, 0.0, 0.0)
        glClearDepth(1.0)
        glEnable(GL_DEPTH_TEST)
        glDepthFunc(GL_LEQUAL)
        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
        #glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient)
        #glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse)
        #glLightfv(GL_LIGHT1, GL_POSITION, light_position)
        #glEnable(GL_LIGHT1)
        #glEnable(GL_LIGHTING)
        glEnable(GL_CULL_FACE)
        #glEnable(GL_POINT_SMOOTH)
        #glBlendFunc(GL_SRC_ALPHA,GL_ONE)
        glAlphaFunc(GL_GREATER, 0.2)

    def resize(self, (width, height)):
        if height==0:
            height=1
        glViewport(0, 0, width, height)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(45, 1.0*width/height, 0.1, 200.0)
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()

    def printstuff(self):
        print self.ship.position, self.ship.right, self.ship.up, self.ship.target
        #print self.ship.position.dot_product(self.ship.right)
        print self.ship.target.dot_product(self.ship.right)
        print self.ship.target.dot_product(self.ship.up)
        print self.ship.right.dot_product(self.ship.up)

    def draw(self):
        self.set_position()
        glPushMatrix()
        glLoadIdentity()
        glTranslatef(0, 0, -20)
        glCallList(self.crosshair)
        
        glPopMatrix()
        self.starfield.draw()
        #self.triangle.draw()
        pygame.display.flip()
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

    def set_position(self):
        glLoadIdentity()
        gluLookAt(0, 0, 0,
                  self.ship.target.x, self.ship.target.y, self.ship.target.z,
                  self.ship.up.x, self.ship.up.y, self.ship.up.z)
        self.starfield.draw()
        glTranslatef(self.ship.position.x, self.ship.position.y, self.ship.position.z)

    def load(filename=None):
        pass # May not need this.

    def make_crosshair(self, fname='textures/crosshair.png'):
        textures = [0,0]
        texturefile = os.path.join(fname)
        textureSurface = pygame.image.load(texturefile)
        textureData = pygame.image.tostring(textureSurface, "RGBX", 1)
        glBindTexture(GL_TEXTURE_2D, textures[0])
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureSurface.get_width(),
            textureSurface.get_height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
            textureData)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
        self.crosshair = glGenLists(1)
        glNewList(self.crosshair, GL_COMPILE)
        glEnable(GL_ALPHA_TEST)
        glDisable(GL_DEPTH_TEST)
        glBindTexture(GL_TEXTURE_2D, textures[0])
        glBegin(GL_QUADS)

        # Front Face (note that the texture's corners have to match the quad'scorners)
        glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0,  1.0)    # Bottom Left Of The Texture and Quad
        glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0,  1.0)    # Bottom Right Of The Texture and Quad
        glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0,  1.0)    # Top Right Of The Texture and Quad
        glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0,  1.0)    # Top Left Of The Texture and Quad

        glEnd()
        glEnable(GL_DEPTH_TEST)
        glDisable(GL_ALPHA_TEST)
        glEndList()
        print self.crosshair

def make_triangle():
    triangle = glGenLists(1)
    glNewList(triangle, GL_COMPILE)
    
    glBegin(GL_TRIANGLES)
    glColor3f(0.6, 0.1, 0.6)
    glVertex3f(0.0, 1.0, 0.0)
    glColor3f(0.0, 1.0, 0.0)
    glVertex3f(-1.0, -1.0, 0)
    glColor3f(0.0, 0.0, 1.0)
    glVertex3f(1.0, -1.0, 0)
    glEnd()
    
    glEndList()
    return triangle

class Triangle(object):
    def __init__(self, angular_vel):
        self._angular_vel = angular_vel
        self._rot = 0.0

    def get_rot(self):
        if self._rot < self._angle:
            self._rot += self._angular_vel
            return self._rot
        return self._angle

    def set_rot(self, angle):
        self._rot = 0.0
        self._angle = angle

    rotation = property(get_rot, set_rot)

    def draw(self):
        #glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        glPushMatrix()
        glTranslatef(-1.5, 0.0, -6.0)
        #glRotatef(self.rotation, 0.4, 1.0, 0.0)
        glBegin(GL_TRIANGLES)
        glColor3f(0.6, 0.1, 0.6)
        glVertex3f(0.0, 1.0, 0.0)
        glColor3f(0.0, 1.0, 0.0)
        glVertex3f(-1.0, -1.0, 0)
        glColor3f(0.0, 0.0, 1.0)
        glVertex3f(1.0, -1.0, 0)
        glEnd()
        glPopMatrix()

class StarField(object):#psyco.compact):
    def __init__(self, number, colour, depth, position, resolution):
        self.colour = colour
        self.depth = depth
        self.position = position
        self.aspect = resolution[0]/float(resolution[1])
        self.y = -self.depth*math.tan((math.pi/180)*22.5)
        coords = self.get_3dcoords()
        self.coords = [coords.next() for star in range(number)]
        self.draw_init()


    def get_3dcoords(self):
        x = self.aspect*self.y
        self.x = x
        while 1:
            yield [random.uniform(-x, x),
                random.uniform(-self.y, self.y),
                random.uniform(-self.depth, self.depth)]

    def draw(self):
        glDisable(GL_DEPTH_TEST)
        glCallList(self.stars)
        glEnable(GL_DEPTH_TEST)

    def draw_init(self):
        self.stars = glGenLists(1)
        glNewList(self.stars, GL_COMPILE)

        glBegin(GL_POINTS)
        glColor3f(self.colour[0], self.colour[1], self.colour[2])
        for x, y, z in self.coords:
            glVertex3f(x, y, z)
        glEnd()
        #glColor4b(0, 0, 0, 0)
        glEndList()
